#ifndef __ZEDITBOX_HPP
#define __ZEDITBOX_HPP

#include "ZUtf8Char.hpp"
#include "../../datastructureZ/LinkedListZ.h"
#include "../../common.hpp"
#include "../ui.hpp"
#include <vector>

using namespace std;
using namespace datastructureZ;

class ZEditBox
{
	public:typedef LinkedList<ZUtf8Char> LineType;
	public:typedef vector<LineType*>	 Lines;
	
	private:size_t size[2];		//编辑框在屏幕中的大小 x, y
	private:size_t location[2];	//编辑框在屏幕上的位置 x, y
	//private:size_t loc_offset[2];
	
	private:size_t relativeCursorPosition[2]; //跟踪光标的坐标（视觉列x，逻辑行y）
	
	private:Lines content;		//内容
	private:LineType::iterator current; //光标迭代器
	
	private:size_t pageStartLine; //起始显示行
	private:size_t pageStartCol;  //起始显示列
	
	private:char* spaceLine;
	private:char* boxLine;
	
	private:bool changed;
	private:bool visibility;
	
	private:Ui::ActiveBase* fatherPtr;
	
	public:ZEditBox(size_t locX, size_t locY, size_t sizeX, size_t sizeY, Ui::ActiveBase* father):
	size{sizeX, sizeY}, location{locX, locY}, /*loc_offset{0, 0}, */relativeCursorPosition{0, 0}, 
	pageStartLine(0), pageStartCol(0), spaceLine(nullptr), 
	boxLine(nullptr), changed(false), visibility(false), fatherPtr(father)
	{
		if(sizeX < 10  ||  sizeY < 1) throw "Too small";
		
		LineType* firstLine = new LineType;
		content.push_back(firstLine);
		
		current = firstLine->begin();
		
		spaceLine = new char[size[0] + 1];
		memset(spaceLine, ' ', size[0]);
		spaceLine[size[0]] = '\0';
		
		boxLine = new char[size[0] + 1];
		memset(boxLine, '=', size[0]);
		boxLine[size[0]] = '\0';
		boxLine[0] = boxLine[size[0] - 1] = ' ';
	}
	
	public:bool visible(void)
	{
		return visibility;
	}
	
	private:void disable(void)
	{
		for
		(
			Lines::const_iterator i = content.cbegin();
			i != content.cend();
			++i
		)	delete *i;
	}
	
	public:~ZEditBox(void)
	{
		if(spaceLine != nullptr) delete spaceLine;
		if(boxLine != nullptr)	 delete boxLine;
		disable();
	}
	
	//清除内容
	public:void clear(void)
	{
		disable();
		relativeCursorPosition[0] = relativeCursorPosition[1] = 0;
		pageStartLine = pageStartCol = 0;
		
		content.clear();
		
		LineType* firstLine = new LineType;
		content.push_back(firstLine);
		
		current = firstLine->begin();
	}
	
	//清屏（用于刷新）
	private:void clean(void)
	{
		for(size_t i = 1; i + 1 < size[1]; ++i)
		{
			GOTOXY(location[0], location[1] + i);
			cout << spaceLine;
		}
		cout << flush;
	}
	
	public:void resize(size_t x = 0, size_t y = 0)
	{
		if(x != 0) size[0] = x;
		{
			delete boxLine;
			delete spaceLine;
			
			spaceLine = new char[size[0] + 1];
			memset(spaceLine, ' ', size[0]);
			spaceLine[size[0]] = '\0';
			
			boxLine = new char[size[0] + 1];
			memset(boxLine, '=', size[0]);
			boxLine[size[0]] = '\0';
			boxLine[0] = boxLine[size[0] - 1] = ' ';
		}
		if(y != 0) size[1] = y;
	}
	
	public:size_t getSize_X(void) {return size[0];}
	public:size_t getSize_Y(void) {return size[1];}
	
	public:void printNew(bool adjustScreen = true)
	{
		while(_printNew(adjustScreen));
	}
	
	private:bool _printNew(bool adjustScreen = true)
	{
		//清屏
		clean();
		//输出上边框等号线
		GOTOXY(location[0], location[1]);
		cout << White << boxLine << colorEnd;
		//来到内容第一行
		GOTOXY(location[0], location[1] + 1);
		
		//跟踪当前输出位置（控件视觉绝对坐标x, y，GOTOXY需要提供绝对坐标）
		size_t currentPos[2] = {location[0], location[1] + 1};
		size_t width = 1, compressedTabWidth = 0;
		
		//总行数
		size_t lineCount = content.size();
		
		//纵向调整视区
		if(adjustScreen)
		{
			if(relativeCursorPosition[1] + 2 >= pageStartLine + size[1])
			{
				pageStartLine = relativeCursorPosition[1] + 3 - size[1];
			}
			if(relativeCursorPosition[1] < pageStartLine)
			{
				pageStartLine = relativeCursorPosition[1];
			}
		}
		
		//遍历迭代器
		LineType::iterator eachChar;
		//横向输出了current位置的flag
		bool flag = false;
		for
		(	//对于每一行
			size_t lineIndex = pageStartLine; //从页起始显示行开始显示
			lineIndex < lineCount  &&  lineIndex + 2 < pageStartLine + size[1]; //显示页高-2项
			++lineIndex
		)
		{
			//输出左边框
			cout << White << '|' << colorEnd;
			++currentPos[0];
			//从第1列移到页起始显示列
			eachChar = content[lineIndex]->begin();
			size_t startCol = 0;
			for(; startCol < pageStartCol; ++eachChar)
			{//对于每个字符
				//横向调整视区
				if(eachChar == current  &&  adjustScreen)
				{
					if(startCol > 3) pageStartCol = startCol - 3;
					else pageStartCol = 0;
					return true;
				}
				
				//如果还没到达页起始显示列，就已经到达行尾，退出当前行
				if(eachChar.isEnd()) goto nextln;
				//记载跳过的宽幅
				startCol += eachChar->getWidth(startCol);
			}
			
			compressedTabWidth = 0;
			
			//多宽幅字符对齐
			if(startCol > pageStartCol)
			{
				size_t diff = startCol - pageStartCol;
				for(size_t i = 0; i < diff; ++i) //对于tab可能会有8格宽度
				{//如果跳过的字符宽幅超过了起始列，进行对齐
					cout << ' ';
				}
				currentPos[0] += diff;
				compressedTabWidth += diff;
			}
			
			//对于一行中的每个字符
			while(!eachChar.isEnd())
			{
				//计算字符的显示宽度
				//制表符宽度单独计算：8 - (n%8)
				if(*eachChar == '\t')
				{
					width = 8 - (startCol % 8);
					eachChar->updateTabCharWidth(width);
					compressedTabWidth += 8 - width;
				}
				else width = eachChar->getWidth();
				
				//如果显示宽度大于等于编辑框宽度，退出当前行（+1为跳过边缘）
				if(width + currentPos[0] + 1 >= location[0] + size[0])
				{
					//横向调整视区
					if(lineIndex == relativeCursorPosition[1]  &&  adjustScreen  &&  !flag)
					{
						pageStartCol = relativeCursorPosition[0] + 3 - 
							size[0] + current->getWidth(relativeCursorPosition[0]) + 
							compressedTabWidth;
						return true;
					}
					else break;
				}
				   
				//输出字符（字符可能被光标所高亮选中）
				if(eachChar == current)
				{
					cout << bgWhite << *(eachChar++) << colorEnd;
					flag = true;
				}
				else cout << *(eachChar++);
				
				//当前输出位置后移字符宽度
				currentPos[0] += width;
				startCol += width;
				
				//调整输出光标位置
				GOTOXY(currentPos[0], currentPos[1]);
			}
			
			//准备进入下一行
			nextln:
			//若光标置于行末，单独高亮显示
			if(eachChar == current  &&  eachChar.isEnd())
			{
				cout << bgWhite << ' ' << colorEnd;
				++currentPos[0];
				flag = true;
			}
			
			//输出右边框
			GOTOXY(location[0] + size[0] - 1, currentPos[1]);
			cout << White << '|' << colorEnd;
			//跟踪输出绝对坐标到下一行首列
			++currentPos[1];
			currentPos[0] = location[0];
			//来到下一行首
			GOTOXY(location[0], currentPos[1]);
		}
		//输出空行和每个空行的左右边框
		while(currentPos[1] + 1 < location[1] + size[1])
		{
			cout << White << '|' << colorEnd;
			GOTOXY(location[0] + size[0] - 1, currentPos[1]);
			cout << White << '|' << colorEnd;
			++currentPos[1];
			GOTOXY(location[0], currentPos[1]);
		}
		//输出下边框等号线
		GOTOXY(location[0], location[1] + size[1] - 1);
		cout << White << boxLine << colorEnd << flush;
		#ifdef DEBUGZEB
		GOTOXY(location[0] + 6, location[1] + size[1] - 1);
		cout << relativeCursorPosition[0] << ", " 
			<< relativeCursorPosition[1] << " | pgSCol: "
			<< pageStartCol << " pgsLn: "
			<< pageStartLine << " comtab: "
			<< compressedTabWidth << " in "
			<< size[0] << ", " << size[1] << flush;
		#endif
		return false;
	}
	
	public:void pgRight(size_t n = 1)
	{
		pageStartCol += n;
	}
	
	public:void pgLeft(size_t n = 1)
	{
		if(pageStartCol == 0 || n > pageStartCol) pageStartCol = 0;
		else pageStartCol -= n;
	}
	
	public:void pgDown(size_t n = 3)
	{
		pageStartLine += n;
	}
	
	public:void pgUp(size_t n = 3)
	{
		if(pageStartLine == 0 || n > pageStartLine) pageStartLine = 0;
		else pageStartLine -= n;
	}
	
	public:void cursorRight(size_t n = 1)
	{
		//链表顺序访问    如果不是在行尾右移光标
		while(n--) if(!current.isEnd())
		{
			//光标列跟踪：加上当前光标所选中字符的宽度
			relativeCursorPosition[0] += current->getWidth();
			//如果光标到达右边框，视区向右移动
			//if(relativeCursorPosition[0] + 1 + current->getWidth() >= pageStartCol + size[0]) pgRight(current->getWidth());
			
			//选中后一个字符
			++current;
		}
		else //行尾处右移光标
		{
			//如果有下一行
			if(relativeCursorPosition[1] + 1 < content.size())
			{	//进入下一行首列
				++relativeCursorPosition[1];
				relativeCursorPosition[0] = 0;
				//视区回到最左边
				//pageStartCol = 0;
				//光标迭代器选中下一行的第一个字符
				current = content[relativeCursorPosition[1]]->begin();
				//如果超出视区范围，视区向下移动
				//if(relativeCursorPosition[1] + 2 >= pageStartLine + size[1]) pgDown(1);
			}
			else break;
		}
	}
	
	public:void cursorLeft(size_t n = 1)
	{
		while(n--) if(current != content[relativeCursorPosition[1]]->begin())
		{
			--current;
			relativeCursorPosition[0] -= current->getWidth();
			//if(relativeCursorPosition[0] + 1 <= pageStartCol) pgLeft(current->getWidth());
		}
		else
		{
			if(relativeCursorPosition[1] > 0)
			{
				--relativeCursorPosition[1];
				relativeCursorPosition[0] = 0;
				current = content[relativeCursorPosition[1]]->end();
				
				for
				(
					LineType::iterator i = content[relativeCursorPosition[1]]->begin();
					i != current;
					++i
				)	relativeCursorPosition[0] += i->getWidth();
				//pageStartCol = relativeCursorPosition[0] > size[0]? relativeCursorPosition[0] - size[0] + 3: 0;
				
			}
			else break;
		}
	}
	
	public:void cursorUp(size_t n = 1)
	{
		//如果n超过了当前所在的行标，回到第一行
		if(relativeCursorPosition[1] >= n) relativeCursorPosition[1] -= n;
		else relativeCursorPosition[1] = 0;
		
		//先来到上一行首列
		current = content[relativeCursorPosition[1]]->begin();
		//调整视区
		//if(relativeCursorPosition[1] < pageStartLine) pgUp(n);
		
		//如果行空（首列即终列）
		if(current.isEnd())
		{
			//光标跟踪
			relativeCursorPosition[0] = 0;
			//调整视区
			//pageStartCol = 0;
			
			return;
		}
		
		//回到之前的列
		//i为当前宽度，j为每个字符宽度
		size_t i = 0, j = 0;
		while(i < relativeCursorPosition[0])
		{
			//如果这一行没有之前所在行那么长，行尾还没到达之前的选中列
			//选中行尾
			if(current.isEnd()) break;
			//宽度跟踪
			i += (j = current->getWidth());
			//光标向后移动
			++current;
		}
		//不要冒进下一个位置
		if(i > relativeCursorPosition[0])
		{	//回滚
			i -= j;
			--current;
		}
		//光标跟踪
		relativeCursorPosition[0] = i;
		
		//调整视区
		/*if(current.isEnd())
		{
			pageStartCol = ((relativeCursorPosition[0] + 2) > size[0]? 
				relativeCursorPosition[0] - size[0] + 3: 0);
		}
		else
		{
			pageStartCol = ((relativeCursorPosition[0] + current->getWidth() + 2) > size[0]? 
				relativeCursorPosition[0] + current->getWidth() - size[0] + 3: 0);
		}*/
	}
	
	public:void cursorDown(size_t n = 1)
	{
		if(relativeCursorPosition[1] + n < content.size()) relativeCursorPosition[1] += n;
		else relativeCursorPosition[1] = content.size() - 1;
		
		current = content[relativeCursorPosition[1]]->begin();
		//if(relativeCursorPosition[1] + 2 >= pageStartLine + size[1]) pgDown(n);
		
		if(current.isEnd())
		{
			relativeCursorPosition[0] = 0;
			//pageStartCol = 0;
			return;
		}
		
		size_t i = 0, j = 0;
		while(i < relativeCursorPosition[0])
		{
			if(current.isEnd()) break;
				
			i += (j = current->getWidth());
			++current;
		}
		
		if(i > relativeCursorPosition[0])
		{
			i -= j;
			--current;
		}
		relativeCursorPosition[0] = i;
		/*if(current.isEnd())
		{
			pageStartCol = ((relativeCursorPosition[0] + 2) > size[0]? 
				relativeCursorPosition[0] - size[0] + 3: 0);
		}
		else
		{
			pageStartCol = ((relativeCursorPosition[0] + current->getWidth() + 2) > size[0]? 
				relativeCursorPosition[0] + current->getWidth() - size[0] + 3: 0);
		}*/
	}
	
	private:bool insert(ZUtf8Char& c)
	{
		if(c == (char)27) return true;		//ESC键
		else if(c == '\n'  ||  c == '\r')	//换行
		{
			//新建行
			LineType* newLine = new LineType;
			
			//将行插入content向量
			if(content.begin() + (relativeCursorPosition[1]++)  ==  content.end()) content.push_back(newLine);
			else content.insert(content.begin() + relativeCursorPosition[1], newLine);
			
			//从原行中间截断
			if(!current.isEnd() && !current.inEmpty())
			{
				LineType::iterator currentLast = current.lList().last();
				LineType::iterator newLineBegin = newLine->begin();
				LineType::pick_down_insert_back(current, currentLast, newLineBegin);
			}
			
			//来到新行，调整视区，跟踪光标
			current = newLine->begin();
			pageStartCol = 0;
			relativeCursorPosition[0] = 0;
		}
		else if(c == (char)127  ||  c == (char)8) //退格
		{
			LineType::iterator temp = current;
			//退格删除的是光标选中字符的前一个字符
			if(!(--temp).isEnd())
			{//如果不是删除行首，普通地在本行中删除一个字符
				//光标前移前一个字符的宽度（tab不确定的宽度由每次printNew计算维护）
				relativeCursorPosition[0] -= temp->getWidth();
				//删除字符
				temp.erase();
				//erase方法删除后会后移，将temp迭代器移回被删除字符的位置
				--temp;
				//调整视区
				/*if(relativeCursorPosition[0] + 1 <= pageStartCol)
				{
					if(temp.isEnd()) pageStartCol = 0;
					else pgLeft(temp->getWidth());
				}*/
			}
			else if(relativeCursorPosition[1] > 0)
			{//否则合并本行和上一行
				//行空标志
				bool flag = true;
				//若本行非空则合并
				if(!temp.inEmpty())
				{
					LineType::iterator currentLineBegin = current.lList().begin();
					LineType::iterator currentLineLast  = current.lList().last();
					
					LineType::iterator preLineEnd = content[relativeCursorPosition[1] - 1]->end();
					
					LineType::pick_down_insert_front(currentLineBegin, currentLineLast, preLineEnd);
					//光标定位到已经合并到上一行末尾的本行首列
					current = currentLineBegin;
					flag = false;
				}
				
				//删除本行
				delete content[relativeCursorPosition[1]];
				content.erase(content.begin() + relativeCursorPosition[1]);
				//来到上一行
				--relativeCursorPosition[1];
				//若本行原先是空的，光标则定位到上一行尾部
				if(flag) current = content[relativeCursorPosition[1]]->end();
				
				//跟踪光标，调整视区
				relativeCursorPosition[0] = 0;
				for
				(
					LineType::iterator i = content[relativeCursorPosition[1]]->begin();
					i != current;
					++i
				)	relativeCursorPosition[0] += i->getWidth();
				//pageStartCol = relativeCursorPosition[0] > size[0]? relativeCursorPosition[0] - size[0] + 3: 0;
				
			}
		}
		else//普通输入
		{
			//计算输入字符的宽度
			if(c == '\t') c.updateTabCharWidth(8 - (relativeCursorPosition[0] % 8));
			size_t wid = c.getWidth();
			//将字符插入到光标前部
			current.insert_to_front(c);
			//跟踪光标
			relativeCursorPosition[0] += wid;
			//调整视区
			//if(relativeCursorPosition[0] + 2 >= pageStartCol + size[0]) pgRight(wid);
		}
		
		//调整视区（纵向）
		//if(relativeCursorPosition[1] + 2 >= pageStartLine + size[1]) pgDown(1);
		//将状态设为 已经修改过
		changed = true;
		return false;
	}
	
	//生成消息编辑结果字符串
	private:string dmp(void)
	{
		string result("");
		bool firstLine = true;
		for
		(
			Lines::const_iterator i = content.cbegin();
			i != content.cend();
			++i
		)
		{
			if(!firstLine) result += '\n';
			for
			(
				LineType::iterator eachC = (*i)->begin();
				!eachC.isEnd();
				++eachC
			)
			{
				result.append((const char*)(*eachC), eachC->getMemorySize());
			}
			firstLine = false;
		}
		return result;
	}
	
	private:void insertMode(bool refresh = true)
	{
		for(ZUtf8Char c;;)
		{
			if(refresh) printNew();
			c.get(getch);
			if(insert(c) == true) return;
		}
	}
	
	public:void run(string& msgText)
	{
		visibility = true;
		changed = false;
		string opt;
		printNew();
		
		for(ZUtf8Char c;;)
		{
			GOTOXY(location[0] + 1, location[1] + size[1] - 2);
			c.get(getche);
			
			if(c == 'i') insertMode();
			else if(c == '$') insertMode(false);
			else if(c == (char)27)
			{
				//方向键
				if(getch() == 91) switch(getch())
				{
					case 65: cursorUp();	break;
					case 66: cursorDown();	break;
					case 67: cursorRight();	break;
					case 68: cursorLeft();	break;
				}
			}
			else if(c == (char)91)
			{
				//方向键
				switch(getch())
				{
					case 65: cursorUp();	break;
					case 66: cursorDown();	break;
					case 67: cursorRight();	break;
					case 68: cursorLeft();	break;
				}
				insertMode();
			}
			else if(c == ':')
			{
				GOTOXY(location[0], location[1] + size[1] - 2);
				cout << spaceLine;
				GOTOXY(location[0], location[1] + size[1] - 2);
				cout << White << '|' << colorEnd;
				GOTOXY(location[0] + size[0] - 1, location[1] + size[1] - 2);
				cout << White << '|' << colorEnd;
				GOTOXY(location[0] + 1, location[1] + size[1] - 2);
				cout << ':';
				getline(cin, opt);
				if(opt[0] >= '0'  &&  opt[0] <= '9')
				{
					size_t targetLine = toSize_t(opt) - 1;
					if(targetLine < content.size())
					{
						relativeCursorPosition[0] = 0;
						relativeCursorPosition[1] = targetLine;
						current = content[targetLine]->begin();
						pageStartCol  = 0;
						pageStartLine = targetLine - getMin(targetLine, size[1]/2);
					}
				}
				if(opt[0] == 'w') 
				{
					msgText = dmp();
					changed = false;
					if(opt.length() > 1  &&  opt[1] == 'q') break;
				}
				else if(opt[0] == 'q')
				{
					if(!changed) break;
					else if(opt.length() > 1  &&  opt[1] == '!') break;
					
					GOTOXY(location[0], location[1] + size[1] - 2);
					cout << spaceLine;
					GOTOXY(location[0], location[1] + size[1] - 2);
					cout << White << '|' << colorEnd;
					GOTOXY(location[0] + size[0] - 1, location[1] + size[1] - 2);
					cout << White << '|' << colorEnd;
					GOTOXY(location[0] + 4, location[1] + size[1] - 2);
					cout << "Text changed has not saved.";
					continue;
				}
				else if(opt == "clear") clear();
			}
			else if(c == '+') 
			{
				int ch = getche();
				size_t i = 0;
				while(ch >= '0'  &&  ch <= '9')
				{
					i *= 10;
					i += (ch - '0');
					ch = getche();
				}
				switch(ch)
				{
					case 'h': resize(size[0], size[1]+i); break;
					case 'w': resize(size[0]+i, size[1]); break;
					case 'H': location[1] += i; break;
					case 'W': location[0] += i; break;
					default: break;
				}
				fatherPtr->printNew();
				continue;
			}
			else if(c == '-') 
			{
				int ch = getche();
				size_t i = 0;
				while(ch >= '0'  &&  ch <= '9')
				{
					i *= 10;
					i += (ch - '0');
					ch = getche();
				}
				switch(ch)
				{
					case 'h': resize(size[0], size[1]-i); break;
					case 'w': resize(size[0]-i, size[1]); break;
					case 'H': if(location[1] > 2 + i) location[1] -= i; break;
					case 'W': if(location[0] > i) location[0] -= i; break;
					default: break;
				}
				fatherPtr->printNew();
				continue;
			}
			else if(c == CTRLF) {pgLeft(8);		printNew(false); continue;}
			else if(c == CTRRG) {pgRight(8);	printNew(false); continue;}
			else if(c == CTRUP) {pgUp(3);		printNew(false); continue;}
			else if(c == CTRDW) {pgDown(3);		printNew(false); continue;}
			else if(c == CTRCF)
			{
				GOTOXY(location[0], location[1] + size[1] - 2);
				cout << spaceLine;
				GOTOXY(location[0], location[1] + size[1] - 2);
				cout << White << '|' << colorEnd;
				GOTOXY(location[0] + size[0] - 1, location[1] + size[1] - 2);
				cout << White << '|' << colorEnd;
				GOTOXY(location[0] + 4, location[1] + size[1] - 2);
				cout << "This is the " << relativeCursorPosition[1] + 1 << "th line.";
				continue;
			}
			else if(c == 'd')
			{
				c.get(getche);
				if(c == 'd')
				{
					//如果不是第一行
					if(relativeCursorPosition[1] > 0)
					{	//删除本行
						delete content[relativeCursorPosition[1]];
						content.erase(content.begin() + relativeCursorPosition[1]);
						cursorUp();
					}
					else//如果是第一行
					{	//清空本行
						content[relativeCursorPosition[1]]->clear();
						relativeCursorPosition[0] = pageStartCol = 0;
						current = content[relativeCursorPosition[1]]->begin();
					}
					changed = true;
				}
			}
			else if(c == 'G') 
			{
				cursorDown(content.size() - relativeCursorPosition[1] - 1);
			}
			else if(c == 'o')
			{
				//新建行
				LineType* newLine = new LineType;
			
				//将行插入content向量
				if(content.begin() + (relativeCursorPosition[1]++)  ==  content.end()) content.push_back(newLine);
				else content.insert(content.begin() + relativeCursorPosition[1], newLine);
			
				current = newLine->begin();
				pageStartCol = 0;
				relativeCursorPosition[0] = 0;
				
				insertMode();
			}
			printNew();
		}
		
		visibility = false;
	}
};

#endif
